package idv.takeshi.software.productline.bookstore.domain.model.shoppingcart;

import idv.takeshi.software.productline.bookstore.domain.model.book.Book;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * A model class for representing as a ShoppingCart used for user of on-line bookstore.
 * @author takeshi
 *
 */
public class ShoppingCart {
 
	private List<ShoppingCartItem> shoppingCartItems = new ArrayList<ShoppingCartItem>();
	 
	/**
	 * Add a book into this ShoppingCart.
	 * Here are two rules as following,<br>
	 * <ol>
	 * 	<li>if contains this book, increment one quantity for this book.</li>
	 * 	<li>if doesn't contain this book, add one ShoppingCartItem with this book 
	 * 	and give it quantity one.</li>
	 * </ol>
	 * @param book
	 */
	public void addBook(Book book) {
		ShoppingCartItem item = null;
		item = getShoppingCartItem(book);
		if(null != item){
			item.incrementQuantity();
		} else{
			if(null != book){
				item = new ShoppingCartItem();
				item.setBook(book);
				item.setQuantity(1);
				this.shoppingCartItems.add(item);
			}
		}
	}
	 
	/**
	 * Update quantity by given book and corresponding quantity to update.<br>
	 * Here are two rules as following<br>
	 * <ol>
	 * 	<li>if quantity <= 0, then remove the corresponding shoppingCartItem</li>
	 * 	<li>if quantity > 0, then find the corresponding ShoppingCartItem and 
	 * 	update it's quantity to given quantity.</li>
	 * </ol>
	 * @param book
	 * @param quantity
	 */
	public void updateQuantity(Book book, int quantity) {
		ShoppingCartItem item = this.getShoppingCartItem(book);
		if(null != item){
			if(quantity > 0){
				item.setQuantity(quantity);
			} else{
				this.shoppingCartItems.remove(item);
			}
		} else{
			if(null != book && quantity > 0){
				item = new ShoppingCartItem();
				item.setBook(book);
				item.setQuantity(quantity);
				this.shoppingCartItems.add(item);
			}
		}
	}
	
	/**
	 * Get total quantity.
	 * @return
	 */
	public int getTotalQuantity(){
		int quantity = 0;
		for(ShoppingCartItem item : this.shoppingCartItems){
			quantity += item.getQuantity();
		}
		return quantity;
	}
	
	/**
	 * Get total price.
	 * @return
	 */
	public BigDecimal getTotalPrice(){
		BigDecimal price = new BigDecimal("0");
		for(ShoppingCartItem item : this.shoppingCartItems){
			price = price.add(item.getTotalPrice());
		}
		return price;
	}
	
	 
	/**
	 * Remove shoppingCartItem by given book.
	 * @param book
	 */
	public void removeBook(final Book book) {
		ShoppingCartItem item = null;
		item = getShoppingCartItem(book);
		if(null != item) this.shoppingCartItems.remove(item);
	}

	/**
	 * Get ShoppingCartItem by given book.
	 * @param book
	 * @return
	 */
	public ShoppingCartItem getShoppingCartItem(final Book book) {
		ShoppingCartItem item = null;
		item = (ShoppingCartItem) CollectionUtils.find(
			this.shoppingCartItems, new Predicate(){
				
				public boolean evaluate(Object object) {
					ShoppingCartItem item = (ShoppingCartItem) object;
					return null != item? item.getBook().equals(book): false;
				}}
			);
		return item;
	}
	 
	/**
	 * Get unmodifiable ShoppingCartItems.
	 * @return
	 */
	public List<ShoppingCartItem> getShoppingCartItems() {
		return Collections.unmodifiableList(this.shoppingCartItems);
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj == null) { return false; }
		   if (obj == this) { return true; }
		   if (obj.getClass() != getClass()) {
		     return false;
		   }
		   ShoppingCart rhs = (ShoppingCart) obj;
		   return new EqualsBuilder()
			   .append(this.shoppingCartItems, rhs.shoppingCartItems)
		       .isEquals();
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		return new HashCodeBuilder(89, 97).
	       append(this.shoppingCartItems).
	       toHashCode();
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
	}
	
	
	 
}
 
